//备注：拷贝代码请加上作者信息
//作者：王海涛
//邮箱：1126471088@qq.com
//版本：V0.2.0
#include "soft_spi_bsp.h"
#include <string.h>
#include <stdlib.h>


typedef struct
{
    void (*GPIO_Init_CB)(void);
    void (*GPIO_Set_State_CB)(WHT_Soft_SPI_BUS_GPIO_enum io, WHT_Sort_SPI_BUS_IO_State_enum state);//设置引脚状态
    WHT_Sort_SPI_BUS_IO_State_enum (*GPIO_Get_State_CB)(WHT_Soft_SPI_BUS_GPIO_enum io);            //获取引脚状态
}WHT_SPI_GPIO_t;

/*软SPI总线结构体*/
typedef struct
{
    WHT_Soft_SPI_BUS_t Info; //总线信息
    unsigned int Delay_Count;//延迟时间
    WHT_SPI_GPIO_t GPIO;     //总线GPIO
}WHT_SPI_BUS_Private_t;

#define WHT_BUS        ((WHT_SPI_BUS_Private_t*)bus)

#ifndef NULL
#define NULL ((void*)0)
#endif

static WHT_SPI_BUS_Private_t* WHT_SPI_BUS[3] = {0};//SPI总线3个


/*软SPI波特率延迟控制*/
#if 1
static void WHT_Soft_SPI_Delay(WHT_Soft_SPI_BUS_t* bus)
{
    unsigned int count;

    count = WHT_BUS->Delay_Count;
    while (count--);
}
#else
#define WHT_Soft_SPI_Delay(...)
#endif
/*软SPI收发N位*/
static unsigned int WHT_Soft_SPI_Read_Write_NBit(WHT_Soft_SPI_BUS_t* bus, const unsigned int datax)
{
    unsigned int Rx_Data = 0;
    unsigned char Bit_Wide = WHT_BUS->Info.Config.Bit_Wide - 1;

    switch (WHT_BUS->Info.Config.Work_Mode)
    {
    case Sort_SPI_Work_Mode0:
        for (unsigned int i = 1 << Bit_Wide; i != 0; i >>= 1)//MSB
        {
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);//拉低时钟
            if (datax & i)
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Hig);
            else
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Low);
            WHT_Soft_SPI_Delay(bus);
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);//拉高时钟
            if (WHT_BUS->GPIO.GPIO_Get_State_CB(Soft_SPI_MISO) == Soft_SPI_IO_Hig)
                Rx_Data |= i;
            WHT_Soft_SPI_Delay(bus);
        }
        WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);//拉低时钟
        break;
    case Sort_SPI_Work_Mode1:
        for (unsigned int i = 1 << Bit_Wide; i != 0; i >>= 1)//MSB
        {
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);//拉高时钟
            if (datax & i)
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Hig);
            else
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Low);
            WHT_Soft_SPI_Delay(bus);
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);//拉低时钟
            if (WHT_BUS->GPIO.GPIO_Get_State_CB(Soft_SPI_MISO) == Soft_SPI_IO_Hig)
                Rx_Data |= i;
            WHT_Soft_SPI_Delay(bus);
        }
        break;
    case Sort_SPI_Work_Mode2:
        for (unsigned int i = 1 << Bit_Wide; i != 0; i >>= 1)//MSB
        {
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);//拉高时钟
            if (datax & i)
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Hig);
            else
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Low);
            WHT_Soft_SPI_Delay(bus);
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);//拉低时钟
            if (WHT_BUS->GPIO.GPIO_Get_State_CB(Soft_SPI_MISO) == Soft_SPI_IO_Hig)
                Rx_Data |= i;
            WHT_Soft_SPI_Delay(bus);
        }
        WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);//拉高时钟
        break;
    case Sort_SPI_Work_Mode3:
        for (unsigned int i = 1 << Bit_Wide; i != 0; i >>= 1)//MSB
        {
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);//拉低时钟
            if (datax & i)
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Hig);
            else
                WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Low);
            WHT_Soft_SPI_Delay(bus);
            WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);//拉高时钟
            if (WHT_BUS->GPIO.GPIO_Get_State_CB(Soft_SPI_MISO) == Soft_SPI_IO_Hig)
                Rx_Data |= i;
            WHT_Soft_SPI_Delay(bus);
        }
        break;
    }
    return Rx_Data;
}/*软SPI收发N位*/
/*软SPI发送或接收数据*/
static void WHT_Soft_SPI_Read_Write(WHT_Soft_SPI_BUS_t* bus, WHT_Soft_SPI_Cache_t* cache)
{
    *(WHT_Soft_SPI_BUS_Lock_enum*)&WHT_BUS->Info.Mutex = Soft_SPI_Lock;
    *(WHT_Soft_SPI_BUS_State_enum*)&WHT_BUS->Info.State = Soft_SPI_No_Error;

    if (cache->Buffer_Count == 0)
        goto Game_Over;
    
    //检测总线状态
    #if 0
    if (WHT_BUS->GPIO.GPIO_Get_State_CB(Soft_SPI_CS) == Soft_SPI_IO_Low)
    {
        (WHT_Soft_SPI_BUS_State_enum)WHT_BUS->Info.State = Soft_SPI_BUS_Error;
        goto Game_Over;
    }
    #endif
    
    cache->Set_NSS_State_CB(Soft_SPI_IO_Low);//发送结束信号
    //只写
    if (cache->Dir == Soft_SPI_WO)
    {
        //发送数据
        if (WHT_BUS->Info.Config.Bit_Wide / (8 + 1) == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                WHT_Soft_SPI_Read_Write_NBit(bus, cache->Tx_Buffer[count]);
        else if (WHT_BUS->Info.Config.Bit_Wide / (16 + 1) == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                WHT_Soft_SPI_Read_Write_NBit(bus, ((unsigned short*)cache->Tx_Buffer)[count]);
        else if (WHT_BUS->Info.Config.Bit_Wide / (32 + 1) == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                WHT_Soft_SPI_Read_Write_NBit(bus, ((unsigned int*)cache->Tx_Buffer)[count]);
    }
    else if (cache->Dir == Soft_SPI_RO)
    {
        #define Soft_SPI_Dummy   0
        //读取数据
        if (WHT_BUS->Info.Config.Bit_Wide / 9 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned char*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, Soft_SPI_Dummy);
        else if (WHT_BUS->Info.Config.Bit_Wide / 17 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned short*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, Soft_SPI_Dummy);
        else if (WHT_BUS->Info.Config.Bit_Wide / 33 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned int*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, Soft_SPI_Dummy);
    }
    else if (cache->Dir == Soft_SPI_RW)
    {
        //读取和写入数据
        if (WHT_BUS->Info.Config.Bit_Wide / 9 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned char*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, ((unsigned char*)cache->Tx_Buffer)[count]);
        else if (WHT_BUS->Info.Config.Bit_Wide / 17 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned short*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, ((unsigned short*)cache->Tx_Buffer)[count]);
        else if (WHT_BUS->Info.Config.Bit_Wide / 33 == 0)
            for (unsigned int count = 0; count < cache->Buffer_Count; count++)
                ((unsigned int*)cache->Rx_Buffer)[count] = WHT_Soft_SPI_Read_Write_NBit(bus, ((unsigned int*)cache->Tx_Buffer)[count]);
    }
Game_Over:
    cache->Set_NSS_State_CB(Soft_SPI_IO_Hig);//发送结束信号
    *(WHT_Soft_SPI_BUS_Lock_enum*)&WHT_BUS->Info.Mutex = Soft_SPI_Unlock;
}



/*总线速率设置*/
static void WHT_Soft_SPI_BUS_Set_Rate(WHT_Soft_SPI_BUS_t* bus)
{
    WHT_BUS->Delay_Count = (unsigned int)(((float)(WHT_BUS->Info.Config.Sys_Rate_Hz / 2)) / (float)(WHT_BUS->Info.Config.SPI_Rate_Hz) / 4);//延迟时间
}
/*总线初始化*/
static void WHT_Soft_SPI_BUS_Init(WHT_Soft_SPI_BUS_t* bus)
{
    WHT_Soft_SPI_BUS_Set_Rate(bus);
    WHT_BUS->GPIO.GPIO_Init_CB();
    //cache->Set_NSS_State_CB(Soft_SPI_IO_Hig);//由用户初始化配置
    WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_MOSI, Soft_SPI_IO_Hig);
    switch (WHT_BUS->Info.Config.Work_Mode)
    {
    case Sort_SPI_Work_Mode0:
    case Sort_SPI_Work_Mode1:WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Low);break;
    case Sort_SPI_Work_Mode2:
    case Sort_SPI_Work_Mode3:WHT_BUS->GPIO.GPIO_Set_State_CB(Soft_SPI_SCLK, Soft_SPI_IO_Hig);break;
    }
    *(WHT_Soft_SPI_BUS_Lock_enum*)&WHT_BUS->Info.Mutex = Soft_SPI_Unlock;
}
/*注册软件SPI总线*/
static WHT_Soft_SPI_BUS_t* WHT_Soft_SPI_BUS_Register(char* name, WHT_Soft_SPI_BUS_Config_t* config, void (*gpio_init_callback)(void),
                                                    void (*gpio_set_callback)(WHT_Soft_SPI_BUS_GPIO_enum io, WHT_Sort_SPI_BUS_IO_State_enum state),
                                                    WHT_Sort_SPI_BUS_IO_State_enum (*gpio_get_callback)(WHT_Soft_SPI_BUS_GPIO_enum io))
{
    char Pos = 0xff;

    if ((strcmp(name, WHT_Soft_SPI1_Name) == 0) && (WHT_SPI_BUS[0] == NULL))
        Pos = 0;
    else if ((strcmp(name, WHT_Soft_SPI2_Name) == 0) && (WHT_SPI_BUS[1] == NULL))
        Pos = 1;
    else if ((strcmp(name, WHT_Soft_SPI3_Name) == 0) && (WHT_SPI_BUS[2] == NULL))
        Pos = 2;
    else
        return NULL;
    
    WHT_SPI_BUS[Pos] = malloc(sizeof(WHT_SPI_BUS_Private_t));
    if (WHT_SPI_BUS[Pos])
    {
        WHT_SPI_BUS[Pos]->Info.Name = name;
        memcpy(&WHT_SPI_BUS[Pos]->Info, config, sizeof(WHT_Soft_SPI_BUS_Config_t));
        WHT_SPI_BUS[Pos]->GPIO.GPIO_Init_CB = gpio_init_callback;
        WHT_SPI_BUS[Pos]->GPIO.GPIO_Set_State_CB = gpio_set_callback;
        WHT_SPI_BUS[Pos]->GPIO.GPIO_Get_State_CB = gpio_get_callback;
        WHT_Soft_SPI_BUS_Init((WHT_Soft_SPI_BUS_t*)WHT_SPI_BUS[Pos]);
        return (WHT_Soft_SPI_BUS_t*)WHT_SPI_BUS[Pos];
    }
    return NULL;
}
/*注销软件SPI总线*/
static void WHT_Soft_SPI_BUS_Unregister(WHT_Soft_SPI_BUS_t* bus)
{
    if (WHT_SPI_BUS[0] == WHT_BUS)
        WHT_SPI_BUS[0] = NULL;
    else if (WHT_SPI_BUS[1] == WHT_BUS)
        WHT_SPI_BUS[1] = NULL;
    else if (WHT_SPI_BUS[2] == WHT_BUS)
        WHT_SPI_BUS[2] = NULL;
    else
        return;
    free(WHT_BUS);
}


/*全局常量*/
const WHT_Soft_SPI_BUS_OPS_t WHT_Soft_SPI_BUS_OPS =
{
    .Register         = WHT_Soft_SPI_BUS_Register,
    .Unregister       = WHT_Soft_SPI_BUS_Unregister,
    .Set_Rate         = WHT_Soft_SPI_BUS_Set_Rate,
    .Read_Write       = WHT_Soft_SPI_Read_Write,
};
